package edu.gu.hajo.gcl.interp.impl;

import java.util.List;
import java.util.Map;
import java.util.Stack;

import edu.gu.hajo.gcl.cmd.ICommand;
import edu.gu.hajo.gcl.cmd.IMutableCommand;
import edu.gu.hajo.gcl.cmd.factory.CommandFactory;
import edu.gu.hajo.gcl.exception.ExecException;
import edu.gu.hajo.gcl.exception.ParseException;
import edu.gu.hajo.gcl.interp.IInterpreter;
import edu.gu.hajo.gcl.parser.Parser;
import edu.gu.hajo.gcl.script.Script;

/**	
 * Implementing IInterpreter
 * @author hajo
 */
public final class Interpreter implements IInterpreter {

	private String result = "";

	private Mem globalVars = new Mem();

	private CommandFactory commandFactory = new CommandFactory(this);

	private Parser parser = new Parser(this);

	private Stack<CallFrame> stack = new Stack<CallFrame>();

	private boolean tclReturn = false;

	// TODO private int reqLevel = 0;

	private Script script;// Debug 

	public Interpreter() {
	}

	public Interpreter(Script script) {
		this.script = script;
	}

	public void eval() {
		if (script == null) {
			throw new IllegalStateException("Script null");
		}
		this.eval(script);
	}

	public void eval(Script script) {
		while (script.hasNext()) {
			try {
				List<String> words;
				try {
					words = parser.parse(script);
				} catch (ParseException pex) {
					System.out.println(pex.getMessage());
					break;
				}
				ICommand cmd = commandFactory.lookup(words);
				if (cmd != null) {
					cmd.execute();
				} else {
					throw new ExecException("Command not found " + words.get(0));
				}
				// If a return statement is hit
				// this flag is set, we shall
				// not continue this script
				if (tclReturn) {
					break;
				}
			} catch (ExecException eex) {
				System.out.println(eex.getMessage());
			}
		}
	}

	public void setReturn(boolean b) {
		tclReturn = b;
	}
	
	public String getVar(String name) {
		String value = null;
		if (!stack.empty()) {
			CallFrame frame = stack.lastElement();
			value = frame.getVar(name);
		} else {
			value = globalVars.getValue(name);
		}
		if (value == null) {
			throw new ParseException("Variable not found " + name);
		} else {
			return value;
		}
	}

	public void setVar(String name, String value) {
		if (!stack.empty()) {
			CallFrame frame = stack.lastElement();
			frame.setVar(name, value);
		} else {
			globalVars.setValue(name, value);
		}
	}

	public String getResult() {
		return result;
	}

	public void setResult(String result) {
		this.result = result;
	}

	public void pushCallframe(String procName, Map<String, String> localVars) {
		stack.push(new CallFrame(procName, localVars));
	}

	public void popCallframe() {
		stack.pop();
	}

	public void createCommand(String name, 
			IMutableCommand command) {
		commandFactory.insert(name, command);
	}
}
